package com.gonsin.i18n.logic.services;

import com.gonsin.i18n.logic.T;
import com.gonsin.i18n.logic.exceptions.GonsinI18nException;
import com.gonsin.i18n.logic.config.I18nConfig;
import com.gonsin.i18n.logic.utils.IgnoreCaseConcurrentHashMap;
import com.gonsin.i18n.logic.utils.TitleMap;
import com.gonsin.i18n.logic.models.KeywordModel;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.classic.MinimalHttpClient;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.time.Duration;
import java.time.temporal.TemporalUnit;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@Slf4j
public class I18nInstance {

    public static I18nConfig CONFIG = I18nConfig.defaultConfig();
    public static volatile boolean INIT = false;

    /**
     * key = projectName 项目名字
     * value = KeywordContainer 存放该项目拥有的所有语言文件
     */
    private Map<String, KeywordContainer> keywordContainerMap;

    public I18nInstance(){
        keywordContainerMap = new IgnoreCaseConcurrentHashMap<>();
        init();

        // 初始化之后无法再修改配置
//        I18nInstance.INIT = true;
    }

    public void init(){

        // 遍历lang文件
        File langPath = new File(CONFIG.getLangPath());
        File[] langProjectFolders = langPath.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File file, String s) {
                // if(s.endsWith(".json") || s.endsWith(".JSON")){
                //     return true;
                // }
                // return false;
                return file.isDirectory();
            }
        });


        if(langProjectFolders == null || langProjectFolders.length == 0) {
//            keywordContainerMap = new HashMap<>();
            keywordContainerMap = new IgnoreCaseConcurrentHashMap<>();
            return;
        }

        for(File f : langProjectFolders){

            // 文件夹的名字作为项目名字
            keywordContainerMap.put(f.getName(), new KeywordContainer(CONFIG, f));
        }

        INIT = true;

        // TODO 获取配置文件，如果配置了云端服务器，则从云端下载语言json包
//        if(!StringUtils.isEmpty(CONFIG.getCloudAddress())){
//            downloadFileFromCloud(CONFIG.getLangPath(), CONFIG.getCloudAddress());
//        }
    }

    /**
     * 同步云端的字符串到本地，
     * @param langPath
     * @param cloudAddress
     */
    private void downloadFileFromCloud(String langPath, String cloudAddress) {
        // 解决云端与本地冲突的问题
        try(CloseableHttpClient client = HttpClients.createDefault()) {
            String url = "/api/projects";
            url = CONFIG.getCloudAddress() + url;

            HttpGet get = new HttpGet(url);
            List<KeywordFileModel> models = client.execute(get, new HttpClientResponseHandler<List<KeywordFileModel>>() {
                @Override
                public List<KeywordFileModel> handleResponse(ClassicHttpResponse response) throws HttpException, IOException {
                    String json = IOUtils.toString(response.getEntity().getContent());
                    List<KeywordFileModel> models = new Gson().fromJson(json, new TypeToken<List<KeywordFileModel>>(){}.getType());
                    return models;
                }
            });
            for(KeywordFileModel model : models){
                compareWithLocal(model);
            }

        } catch (Exception ex) {
            log.warn(ex.toString(), ex);

        }
    }


    /**
     * 比较本地的资源，，比较规则如下：
     * 如果是本地不存在，云端存在，则直接添加到本地
     * 如果是本地存在，云端不存在，则不处理
     * 如果是本地存在，云端也存在，则用云端的替换本地
     * @param model
     */
    private void compareWithLocal(KeywordFileModel model) {
        KeywordContainer container = keywordContainerMap.get(model.getProjectName());
        // 如果本地不存在，则直接添加
        if(container == null){
            createIfNotExist(model.getProjectName());
            importProjectKeyword(model.getProjectName(), model.getLang(), model);
            return;
        }

        container.putAll(model);
    }


    /**
     * 添加翻译
     * @param fromClient 是否来自前端（如果来自前端，则不自动创建仙姑）
     */
    public void add(String projectName, String lang, String origin, String after, boolean fromClient, String common){
        KeywordContainer container = keywordContainerMap.get(projectName);
        if(fromClient){
            if(container != null){
                // if(container.contains(origin)){
                //     return;
                // }

                container.add(lang, origin, after, true, common);
            }
            return;
        }

        container = keywordContainerMap.computeIfAbsent(projectName, k -> {
            // 创建文件夹
            File projectFolder = new File(CONFIG.getLangPath() + File.separator + projectName);
            if(!projectFolder.exists()){
                projectFolder.mkdirs();
            }

            KeywordContainer keywordContainer = new KeywordContainer(CONFIG, projectFolder);
            return keywordContainer;
        });

        container.add(lang, origin, after, fromClient, common);
    }


    /**
     * 修改翻译
     */
    public void update(String projectName, String lang, String origin, String after){
        KeywordContainer container = keywordContainerMap.get(projectName);
        if(container != null){
            container.update(lang, origin, after);
        }
    }


    /**
     * 默认使用  I18nInstance.INIT 中配置的projectName
     * <p>
     * 根据原文获取翻译后的内容
     *
     * @param lang    语言平台（自定义）
     * @param content 原文本
     * @param common  给翻译人员看的内容
     * @param args    文本内如果包含{}动态变量时需要传递变量值。
     * @return i18n平台翻译后的内容
     */
    public String t(String lang, String content, String common, Object ... args){
        if(!I18nInstance.INIT){
            synchronized (I18nInstance.class){
                if(!I18nInstance.INIT){
                    init();
                }
            }
        }


        KeywordContainer container = keywordContainerMap.get(I18nInstance.CONFIG.getProjectName());
        if(container == null){
            FormattingTuple ft = MessageFormatter.arrayFormat(content, args);
            return ft.getMessage();
        }

        return container.transfer(lang, content, common, args);
    }



    /**
     * 项目的配置项
     * @param config 配置对象
     */
    public static void config(I18nConfig config) {
        if(I18nInstance.INIT){
            throw new GonsinI18nException("重复调用了T.config方法");
        }
        I18nInstance.CONFIG = config;
        // 创建默认目录和语言包
        File langPath = new File(config.getLangPath());
        if(!langPath.exists()){
            langPath.mkdirs();
        }
        if(!StringUtils.isEmpty(config.getProjectName())){
            File projectPath = new File(langPath.getAbsolutePath() + File.separator + config.getProjectName());
            if(!projectPath.exists()) {
                projectPath.mkdirs();
            }

            File langFile = new File(projectPath.getAbsolutePath() + File.separator + config.getDefaultLang() + ".json");
            if(!langFile.exists()){
                try {
                    langFile.createNewFile();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                KeywordFileModel model = new KeywordFileModel();
                String title = TitleMap.get(config.getDefaultLang());
                model.setTitle(title == null ? config.getDefaultLang() : title);
                model.setProjectName(config.getProjectName());
                model.setLang(config.getDefaultLang());
                model.setMarker("gonsin");
                model.setMessages(new HashMap<>());
                try {
                    FileUtils.writeStringToFile(langFile, new GsonBuilder().setPrettyPrinting().create().toJson(model), "UTF8");
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        I18nInstance.getInstance();
    }

    /**
     * 查询该语言所有翻译
     */
    public Map<String, String> keywords(String projectName, String lang) {
        KeywordContainer container = keywordContainerMap.get(projectName);
        if(container == null){
            return new HashMap<>();
        }
        return container.all(lang);
    }


    /**
     * 添加新的语言
     */
    public void addKeyword(KeywordModel model) {

        // 所有平台都添加语言
        keywordContainerMap.forEach((projectName, container) -> {

            // 添加语言
            container.addLang(model);
            // Keywor container = keywordContainerMap.computeIfAbsent(model.getLang(), l -> {
            //     KeywordContainer con = new KeywordContainer(CONFIG, model.getTitle(), model.getLang(), new HashMap<>());
            //     con.save();
            //     return con;
            // });
        });

        // KeywordContainer container = keywordContainerMap.computeIfAbsent(model.getLang(), l -> {
        //     KeywordContainer con = new KeywordContainer(CONFIG, model.getTitle(), model.getLang(), new HashMap<>());
        //     con.save();
        //     return con;
        // });
    }

    /**
     * 查询当前所有添加的语言
     */
    public List<KeywordModel> langs(String projectName) {
        KeywordContainer con = keywordContainerMap.get(projectName);
        return con.allLangs();
        // return keywordContainerMap.values().stream().map(k -> new KeywordModel(k.getTitle(), k.getLang()))
        //         .collect(Collectors.toList());
    }

    private static I18nInstance instance;
    public static I18nInstance getInstance(){
        if(instance == null){
            synchronized (T.class){
                if(instance == null){
                    instance = new I18nInstance();
                }
            }
        }
        return instance;
    }

    public void deleteKeyword(String lang) {

        keywordContainerMap.forEach((projectName, container) -> {

            // 删除语言
            container.deleteLang(lang);
            // Keywor container = keywordContainerMap.computeIfAbsent(model.getLang(), l -> {
            //     KeywordContainer con = new KeywordContainer(CONFIG, model.getTitle(), model.getLang(), new HashMap<>());
            //     con.save();
            //     return con;
            // });

        });

        // KeywordContainer container = keywordContainerMap.get(lang);
        // if(container == null){
        //     return;
        // }
        // container.clean();
        // keywordContainerMap.remove(lang);
    }


    /**
     * 删除
     */
    public void deleteWord(String projectName, String lang, String originWord) {
        KeywordContainer container = keywordContainerMap.get(projectName);
        if(container == null){
            return;
        }
        container.update(lang, originWord, null);
    }


    /**
     * 导出语言包
     * 一次导出所有项目，相同语言的内容
     * @return key = projectName， value = 语言描述文件
     */
    public Map<String, KeywordFileModel> exportKeyword(String lang){
        Map<String, KeywordFileModel> out = new HashMap<>();
        keywordContainerMap.forEach((projectName, container) -> {

            KeywordFile keywordFile = container.copyKeywordFile(lang);
            if(keywordFile == null){
                return;
            }
            out.put(projectName, keywordFile.getModel());

        });

        return out;
    }



    /**
     * 导入语言包
     * 一次导入所有项目的相同语言
     * @param projectMap key = projectName， value = 语言描述文件
     */
    public void importKeyword(Map<String, KeywordFileModel> projectMap) {
        // String json = null;
        // try {
        //     json = FileUtils.readFileToString(file, CONFIG.getEncoder());
        // } catch (IOException e) {
        //     e.printStackTrace();
        //     throw new GonsinI18nException("无法读取【{}】文件", file.getAbsolutePath());
        // }
        // KeywordFile bean = new Gson().fromJson(json, KeywordFile.class);


        projectMap.forEach((projectName, model) -> {
            KeywordContainer container = keywordContainerMap.get(projectName);
            if(container == null){
                return;
            }
            container.putAll(model);
        });
    }

    /**
     * 导入具体项目具体的语言包内容
     * @param projectName 项目名字
     * @param lang 语言名字
     * @param keywordFileModel 语言描述
     */
    public void importProjectKeyword(String projectName, String lang, KeywordFileModel keywordFileModel) {
        KeywordContainer container = keywordContainerMap.get(projectName);
        if(container == null){
            return;
        }
        container.putLang(lang, keywordFileModel);
    }

    public KeywordFileModel exportProjectKeyword(String projectName, String lang) {
        KeywordContainer container = keywordContainerMap.get(projectName);
        if(container == null){
            return null;
        }
        KeywordFile keywordFile = container.copyKeywordFile(lang);
        if(keywordFile == null){
            return null;
        }
        return keywordFile.getModel();

    }


    /**
     * 创建项目
     * @param projectName
     */
    public void createIfNotExist(String projectName) {
        KeywordContainer container = keywordContainerMap.get(projectName);
        if(container == null){
            String defaultProject = CONFIG.getProjectName();
            List<KeywordModel> models = langs(defaultProject);
            File projectFolder = new File(CONFIG.getLangPath() + File.separator + projectName);
            KeywordContainer newContainer = new KeywordContainer(CONFIG, projectFolder);
            for(KeywordModel currentModle : models){
                KeywordModel newModel = new KeywordModel();
                newModel.setLang(currentModle.getLang());
                newModel.setTitle(currentModle.getTitle());
                newContainer.addLang(newModel);
            }
            keywordContainerMap.put(projectName, newContainer);
        }
    }
}
